﻿using System;
using System.Web.Mvc;
using Kratos.Framework.Extensions;
using Kratos.Framework.Repositories.NHibernateProvider;
using NHibernate;

namespace Kratos.Framework.Filters
{
    public class UnitOfWorkAttribute : KratosFilterAttribute
    {
        public UnitOfWorkAttribute() : base(typeof(UnitOfWorkFilter)) { }
    }

    public class UnitOfWorkFilter : IActionFilter
    {
        private readonly ISessionManager _sessionManager;
        private readonly IPerActionTransactionStore _perActionTransactionStore;

        public UnitOfWorkFilter(ISessionManager sessionManager, IPerActionTransactionStore perActionTransactionStore)
        {
            _sessionManager = sessionManager;
            _perActionTransactionStore = perActionTransactionStore;
        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            try
            {
                var session = _sessionManager.Get();
                _perActionTransactionStore.StoreTransaction(filterContext, session.BeginTransaction());
            }
            catch (Exception ex)
            {
                Console.WriteLine("打开session错误:{0}".With(ex.Message));
            }
        }

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            var transaction = _perActionTransactionStore.ResolveTransaction(filterContext);
            if (transaction == null) return;
            
            var hasExceptions = filterContext.Exception == null || filterContext.ExceptionHandled;
            if (filterContext.Controller.ViewData.ModelState.IsValid && hasExceptions)
            {
                transaction.Commit();
            }
            else
            {
                transaction.Rollback();
            }
        }
    }

    public interface IPerActionTransactionStore
    {
        void StoreTransaction(ActionExecutingContext filterContext, ITransaction transaction);
        ITransaction ResolveTransaction(ActionExecutedContext filterContext);
    }

    public class PerActionTransactionStore : IPerActionTransactionStore
    {
        private const string transactionToken = "__transaction__";

        public void StoreTransaction(ActionExecutingContext filterContext, ITransaction transaction)
        {
            var controllerActionName = transactionToken + filterContext.Controller.GetType().Name + "." +
                                       filterContext.ActionDescriptor.ActionName;

            filterContext.RequestContext.HttpContext.Items[controllerActionName] = transaction;
        }

        public ITransaction ResolveTransaction(ActionExecutedContext filterContext)
        {
            var controllerActionName = transactionToken + filterContext.Controller.GetType().Name + "." +
                                       filterContext.ActionDescriptor.ActionName;

            return filterContext.RequestContext.HttpContext.Items[controllerActionName] as ITransaction;
        }
    }

}
